namespace ScrumSprintMonitor.Implementation
{
	#region #using Directives

	using System;
	using System.Collections.Generic;
	using System.ComponentModel;
	using System.Diagnostics;
	using System.Linq;
	using System.Threading;

	using ContinuousLinq.Reactive;

	using Infrastructure;

	#endregion

	[DebuggerDisplay("TeamMember: {DisplayName}")]
	public class TeamMember : ReactiveObject,
	                          ITeamMember
	{
		private readonly BindingList<BurndownDataPoint> burndownDataPointsList = new BindingList<BurndownDataPoint>();
		private readonly ITeamMemberInformation info;
		private readonly ITeamMemberRemainingWorkload remainingWorkload;
		private IEnumerable<IWorkItemAdapter> assignedWorkItems;

		static TeamMember()
		{
			var dependsOn = Register<TeamMember>();

			dependsOn.Call(me => me.UpdateRemainingWork()).OnChanged(me => me.AssignedWorkItems);
		}

		public TeamMember(ITeamMemberInformation teamMemberInfo, IEnumerable<IWorkItemAdapter> assignedWorkItems,
		                  ITeamMemberRemainingWorkload remainingWorkload)
		{
			if (teamMemberInfo == null)
			{
				throw new ArgumentNullException("teamMemberInfo");
			}
			if (remainingWorkload == null)
			{
				throw new ArgumentNullException("remainingWorkload");
			}

			this.info = teamMemberInfo;
			this.assignedWorkItems = assignedWorkItems;
			this.remainingWorkload = remainingWorkload;
			this.remainingWorkload.Initialize(teamMemberInfo);

			AssignedWorkItems = assignedWorkItems;
		}

		private void UpdateRemainingWork()
		{
			int remainingWorkItemCount = 0;
			double remainingSprintWork = 0.0;

			if (this.assignedWorkItems != null)
			{
				var remainingSprintBacklogItems =
					this.assignedWorkItems.Where(
						x => x.Type == WorkItemType.SprintBacklogItem && (x.State == WorkItemState.InProgress || x.State == WorkItemState.NotDone));

				if (remainingSprintBacklogItems.Any())
				{
					Parallel.Invoke(() => remainingWorkItemCount = remainingSprintBacklogItems.Count(),
					                () => remainingSprintWork = remainingSprintBacklogItems.Where(x => !double.IsNaN(x.RemainingWork)).Sum(x => x.RemainingWork));
				}
			}

			RemainingWorkload.WorkItemCount = remainingWorkItemCount;
			RemainingWorkload.SprintWork = remainingSprintWork;
		}

		#region Implementation of ITeamMember

		public string DisplayName
		{
			get
			{
				if (!string.IsNullOrEmpty(this.info.DisplayAlias))
				{
					return this.info.DisplayAlias;
				}

				return this.info.Name;
			}
		}

		public ITeamMemberInformation Information
		{
			[DebuggerStepThrough]
			get { return this.info; }
		}

		public IEnumerable<IWorkItemAdapter> AssignedWorkItems
		{
			[DebuggerStepThrough]
			get { return this.assignedWorkItems; }
			set
			{
				this.assignedWorkItems = (value != null ? new List<IWorkItemAdapter>(value) : null);

				OnPropertyChanged("AssignedWorkItems");
			}
		}

		public ITeamMemberRemainingWorkload RemainingWorkload
		{
			[DebuggerStepThrough]
			get { return this.remainingWorkload; }
		}

		public BindingList<BurndownDataPoint> BurndownDataPoints
		{
			[DebuggerStepThrough]
			get { return this.burndownDataPointsList; }
		}

		#endregion
	}
}